package com.xneure.database.define.table;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.io.IOUtils;
import com.xneure.database.DataBaseType;
import com.xneure.database.define.table.SqlDefinition.DefinitionType;
import com.xneure.utils.SpringUtil;

/**
 * <p>
 * 系统数据库更新升级
 * </p>
 * <p>
 * Copyright: 版权所有<br>
 *
 * @author zhangjun
 * @version 2018年11月2日
 */
public abstract class SystemUpdate{
	private int maxVer = 0;
	private int historyVersion;
	private List<SqlDefinition> sqlDefinitions;
	private DataBaseType type;
	private DataSource dataSource;

	public static SystemUpdate getSystemUpdate(){
		return getSystemUpdate(SpringUtil.getDataSource());
	}

	public static SystemUpdate getSystemUpdate(DataSource dataSource){
		return getSystemUpdate(DataBaseType.identify(dataSource));
	}

	public static SystemUpdate getSystemUpdate(Connection conn){
		return getSystemUpdate(DataBaseType.identify(conn));
	}

	public static SystemUpdate getSystemUpdate(String dbType){
		return SpringUtil.getBean(dbType);
	}

	public static SystemUpdate getSystemUpdate(DataBaseType dbType){
		return getSystemUpdate(dbType.getDbName());
	}

	public SystemUpdate(DataBaseType type){
		this.type = type;
	}

	public DataBaseType getType(){
		return type;
	}

	public int getMaxVersion(){
		return maxVer;
	}

	public abstract boolean checkVersion();

	public String getTableName(){
		return "T_SYS_UPDATEHISTORY";
	}

	protected abstract String getHistoryTableSql();

	public abstract InputStream getInitStream();

	public void executeSql(SqlDefinition[] sqlDefs){
		try{
			Statement statement = getConnection().createStatement();
			for(SqlDefinition sql:sqlDefs){
				for(String str:sql.getSql()){
					if(str.startsWith("#") == false){
						boolean execRes = false;
						try{
							execRes = statement.execute(str);
						}catch(Exception e){
						}
						if(progress(sql.getType(),execRes,sql.getVersion(),str) == false){
							return;
						}
					}
				}
				progress(null,true,0,null);
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	protected Connection getConnection() throws SQLException{
		if(dataSource == null){
			dataSource = SpringUtil.getDataSource();
		}
		return dataSource.getConnection();
	}

	protected abstract boolean progress(DefinitionType type,boolean isOk,int version,String sql);

	public int getHistoryVersion(){
		return historyVersion;
	}

	public void setHistoryVersion(int historyVersion){
		this.historyVersion = historyVersion;
	}

	public void buildVersions(){
		try{
			readFile(DefinitionType.create,getInitStream());
			readFile(DefinitionType.update,getUpdateStream());
		}catch(IOException e){
			e.printStackTrace();
		}
	}

	private void readFile(DefinitionType type,InputStream stream) throws IOException{
		if(null != stream){
			buildVersions(type,IOUtils.readLines(stream,"UTF-8"));
		}
	}

	protected void buildVersions(DefinitionType type,List<String> src){
		try{
			sqlDefinitions = new ArrayList<>();
			maxVer = 0;
			StringBuilder sb = new StringBuilder(2000);
			SqlDefinition sd = null;
			int ver = 0;
			for(String str:src){
				str = str.trim();
				if(str.length() < 2){
					continue;
				}
				if(str.startsWith("--")){
					ver = Integer.parseInt(str.replace("-",""));
					if(ver > maxVer){
						maxVer = ver;
					}
				}else if(str.endsWith(";")){
					sb.append(str);
					sb.setLength(sb.length() - 1);
					if(null == sd){
						sd = new SqlDefinition();
						sd.setDbType(this.type);
						sd.setType(type);
						sqlDefinitions.add(sd);
					}
					sd.setVersion(ver);
					sd.addSql(sb.toString());
					sb.setLength(0);
				}else{
					sb.append(str);
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	public void initDataBase(){
		try{
			executeSql(sqlDefinitions.parallelStream().filter(d-> d.getVersion() > historyVersion && d.getType() == DefinitionType.create)
					.toArray(SqlDefinition[]::new));
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	public void updateDataBase(){
		try{
			executeSql(sqlDefinitions.parallelStream().filter(d-> d.getVersion() > historyVersion && d.getType() == DefinitionType.update)
					.toArray(SqlDefinition[]::new));
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	public abstract InputStream getUpdateStream();

	public void setDataSource(DataSource dataSource){
		this.dataSource = dataSource;
	}
}
